#include "nemu.h"
#include <stdlib.h>
#include <time.h>

#define NR_TLB 64

typedef struct {
	uint32_t tag : 20;
	uint32_t base : 20;
	bool valid;
} tlb_term;

tlb_term tlb[NR_TLB];


void tlb_add(lnaddr_t);
bool page_in_tlb(lnaddr_t);

hwaddr_t page_translate(lnaddr_t addr) {
	if (cpu.PE != 1 || cpu.PG != 1) {
		return addr;
	}
	if (page_in_tlb(addr) == false) {
		tlb_add(addr);
	}
	uint32_t tag = (addr & ~0xfff) >> 12;
	int i;
	for (i = 0; i < NR_TLB; i ++) {
		if (tlb[i].tag == tag) {
			uint32_t base = tlb[i].base;
			return (base << 12) + (addr & 0xfff);
		}
	}
	/* should not reach here */
	assert(0);
	return -1;
}

bool page_in_tlb(lnaddr_t addr) {
	uint32_t tag = (addr & ~0xfff) >> 12;
	int i;
	for (i = 0; i < NR_TLB; i ++) {
		if (tlb[i].tag == tag) {
			return true;
		}
	}
	return false;
}

void tlb_add(lnaddr_t addr) {
	srand(time(0));
	int i, term = rand() % NR_TLB;
	for (i = 0; i < NR_TLB; i ++) {
		if (tlb[i].valid == false) {
			term = i;
			break;
		}
	}
	uint32_t pd_idx = (addr & 0xffc00000) >> 20;
	uint32_t pd_addr = ((uint32_t)cpu.PDB << 12) + pd_idx;
	uint32_t pt_idx = (addr & 0x003ff000) >> 10;
	uint32_t pd_term = hwaddr_read(pd_addr, 4);
	assert((pd_term & 0x1) == 1);
	uint32_t pt_addr = (pd_term & ~0xfff) + pt_idx;
	uint32_t pt_term = hwaddr_read(pt_addr, 4);
	assert((pt_term & 0x1) == 1);
	uint32_t base = pt_term & ~0xfff;
	uint32_t tag = addr & ~0xfff;
	tlb[term].valid = true;
	tlb[term].tag = tag >> 12;
	tlb[term].base = base >> 12;
}

void init_tlb() {
	int i;
	for (i = 0; i < NR_TLB; i ++) {
		tlb[i].valid = false;
	}
}

